﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace NeuroSpeech.Imap
{
	public class TcpConnection : IDisposable
	{

		TcpClient client = new TcpClient();

		Stream stream = null;

		StreamReader reader = null;
		StreamWriter writer = null;

		public bool Debug { get; set; }

		public TextWriter Logger { get; set; }

		public virtual void Connect(string host, int port, bool ssl) {

			if (Debug && Logger == null)
				Logger = Console.Out;

			client.Connect(host, port);

			if (ssl)
			{

				stream = new SslStream(client.GetStream(), false, this.RemoteCertificateValidationCallback);
				((SslStream)stream).AuthenticateAsClient(host);
			}
			else {
				stream = client.GetStream();
			}

			reader = new StreamReader(stream);
			writer = new StreamWriter(stream);
		}

		public void Disconnect() {
			if (stream != null)
			{
				try
				{
					stream.Close();
				}
				catch { }
				stream = null;
			}
			if (client != null)
			{
				try
				{
					client.Close();
				}
				catch { }
				client = null;
			}

		}

		#region method RemoteCertificateValidationCallback

		/// <summary>
		/// This method is called when we need to validate remote server certificate.
		/// </summary>
		/// <param name="sender">Sender.</param>
		/// <param name="certificate">Certificate.</param>
		/// <param name="chain">Certificate chain.</param>
		/// <param name="sslPolicyErrors">SSL policy errors.</param>
		/// <returns>Returns true if certificate validated, otherwise false.</returns>
		private bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
		{
			//// User will handle it.
			//if (m_pCertCallback != null)
			//{
			//	return m_pCertCallback(sender, certificate, chain, sslPolicyErrors);
			//}
			//else
			{
				if (sslPolicyErrors == SslPolicyErrors.None || ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) > 0))
				{
					return true;
				}

				// Do not allow this client to communicate with unauthenticated servers.
				return false;
			}
		}

		#endregion


		public void WriteLine(string line) {
			if(Debug)
				Logger.WriteLine(line);
			writer.WriteLine(line);
			writer.Flush();
		}

		public void Flush() {
			writer.Flush();
		}

		public string ReadLine() {
			string line = reader.ReadLine();
			if (Debug) {
				Logger.WriteLine(line);
			}
			return line;
		}


		#region internal void ReadChars(char[] PartChars,int p,int p_2)
		internal int ReadChars(char[] PartChars, int p, int p_2)
		{
			reader.DiscardBufferedData();
			return reader.ReadBlock(PartChars, p, p_2);
		}
		#endregion


		public void Dispose()
		{
			Disconnect();
		}
	}
}
